import _defineProperty from 'babel-runtime/helpers/defineProperty';
import _extends from 'babel-runtime/helpers/extends';
import _classCallCheck from 'babel-runtime/helpers/classCallCheck';
import set from 'lodash/set';
import createFormField, { isFormField } from './createFormField';
import { flattenFields as _flattenFields, getErrorStrs, startsWith } from './utils';

function partOf(a, b) {
  return b.indexOf(a) === 0 && ['.', '['].indexOf(b[a.length]) !== -1;
}

var FieldsStore = function () {
  function FieldsStore(fields) {
    _classCallCheck(this, FieldsStore);

    _initialiseProps.call(this);

    this.fields = this.flattenFields(fields);
    this.fieldsMeta = {};
  }

  FieldsStore.prototype.updateFields = function updateFields(fields) {
    this.fields = this.flattenFields(fields);
  };

  FieldsStore.prototype.flattenFields = function flattenFields(fields) {
    return _flattenFields(fields, function (_, node) {
      return isFormField(node);
    }, 'You must wrap field data with `createFormField`.');
  };

  FieldsStore.prototype.flattenRegisteredFields = function flattenRegisteredFields(fields) {
    var validFieldsName = this.getAllFieldsName();
    return _flattenFields(fields, function (path) {
      return validFieldsName.indexOf(path) >= 0;
    }, 'You cannot set field before registering it.');
  };

  FieldsStore.prototype.setFields = function setFields(fields) {
    var _this = this;

    var fieldsMeta = this.fieldsMeta;
    var nowFields = _extends({}, this.fields, fields);
    var nowValues = {};
    Object.keys(fieldsMeta).forEach(function (f) {
      return nowValues[f] = _this.getValueFromFields(f, nowFields);
    });
    Object.keys(nowValues).forEach(function (f) {
      var value = nowValues[f];
      var fieldMeta = _this.getFieldMeta(f);
      if (fieldMeta && fieldMeta.normalize) {
        var nowValue = fieldMeta.normalize(value, _this.getValueFromFields(f, _this.fields), nowValues);
        if (nowValue !== value) {
          nowFields[f] = _extends({}, nowFields[f], {
            value: nowValue
          });
        }
      }
    });
    this.fields = nowFields;
  };

  FieldsStore.prototype.resetFields = function resetFields(ns) {
    var fields = this.fields;

    var names = ns ? this.getValidFieldsFullName(ns) : this.getAllFieldsName();
    return names.reduce(function (acc, name) {
      var field = fields[name];
      if (field && 'value' in field) {
        acc[name] = {};
      }
      return acc;
    }, {});
  };

  FieldsStore.prototype.setFieldMeta = function setFieldMeta(name, meta) {
    this.fieldsMeta[name] = meta;
  };

  FieldsStore.prototype.getFieldMeta = function getFieldMeta(name) {
    this.fieldsMeta[name] = this.fieldsMeta[name] || {};
    return this.fieldsMeta[name];
  };

  FieldsStore.prototype.getValueFromFields = function getValueFromFields(name, fields) {
    var field = fields[name];
    if (field && 'value' in field) {
      return field.value;
    }
    var fieldMeta = this.getFieldMeta(name);
    return fieldMeta && fieldMeta.initialValue;
  };

  FieldsStore.prototype.getValidFieldsName = function getValidFieldsName() {
    var _this2 = this;

    var fieldsMeta = this.fieldsMeta;

    return fieldsMeta ? Object.keys(fieldsMeta).filter(function (name) {
      return !_this2.getFieldMeta(name).hidden;
    }) : [];
  };

  FieldsStore.prototype.getAllFieldsName = function getAllFieldsName() {
    var fieldsMeta = this.fieldsMeta;

    return fieldsMeta ? Object.keys(fieldsMeta) : [];
  };

  FieldsStore.prototype.getValidFieldsFullName = function getValidFieldsFullName(maybePartialName) {
    var maybePartialNames = Array.isArray(maybePartialName) ? maybePartialName : [maybePartialName];
    return this.getValidFieldsName().filter(function (fullName) {
      return maybePartialNames.some(function (partialName) {
        return fullName === partialName || startsWith(fullName, partialName) && ['.', '['].indexOf(fullName[partialName.length]) >= 0;
      });
    });
  };

  FieldsStore.prototype.getFieldValuePropValue = function getFieldValuePropValue(fieldMeta) {
    var name = fieldMeta.name,
        getValueProps = fieldMeta.getValueProps,
        valuePropName = fieldMeta.valuePropName;

    var field = this.getField(name);
    var fieldValue = 'value' in field ? field.value : fieldMeta.initialValue;
    if (getValueProps) {
      return getValueProps(fieldValue);
    }
    return _defineProperty({}, valuePropName, fieldValue);
  };

  FieldsStore.prototype.getField = function getField(name) {
    return _extends({}, this.fields[name], {
      name: name
    });
  };

  FieldsStore.prototype.getNotCollectedFields = function getNotCollectedFields() {
    var _this3 = this;

    return this.getValidFieldsName().filter(function (name) {
      return !_this3.fields[name];
    }).map(function (name) {
      return {
        name: name,
        dirty: false,
        value: _this3.getFieldMeta(name).initialValue
      };
    }).reduce(function (acc, field) {
      return set(acc, field.name, createFormField(field));
    }, {});
  };

  FieldsStore.prototype.getNestedAllFields = function getNestedAllFields() {
    var _this4 = this;

    return Object.keys(this.fields).reduce(function (acc, name) {
      return set(acc, name, createFormField(_this4.fields[name]));
    }, this.getNotCollectedFields());
  };

  FieldsStore.prototype.getFieldMember = function getFieldMember(name, member) {
    return this.getField(name)[member];
  };

  FieldsStore.prototype.getNestedFields = function getNestedFields(names, getter) {
    var fields = names || this.getValidFieldsName();
    return fields.reduce(function (acc, f) {
      return set(acc, f, getter(f));
    }, {});
  };

  FieldsStore.prototype.getNestedField = function getNestedField(name, getter) {
    var fullNames = this.getValidFieldsFullName(name);
    if (fullNames.length === 0 || // Not registered
    fullNames.length === 1 && fullNames[0] === name // Name already is full name.
    ) {
        return getter(name);
      }
    var isArrayValue = fullNames[0][name.length] === '[';
    var suffixNameStartIndex = isArrayValue ? name.length : name.length + 1;
    return fullNames.reduce(function (acc, fullName) {
      return set(acc, fullName.slice(suffixNameStartIndex), getter(fullName));
    }, isArrayValue ? [] : {});
  };

  // @private
  // BG: `a` and `a.b` cannot be use in the same form
  FieldsStore.prototype.isValidNestedFieldName = function isValidNestedFieldName(name) {
    var names = this.getAllFieldsName();
    return names.every(function (n) {
      return !partOf(n, name) && !partOf(name, n);
    });
  };

  FieldsStore.prototype.clearField = function clearField(name) {
    delete this.fields[name];
    delete this.fieldsMeta[name];
  };

  return FieldsStore;
}();

var _initialiseProps = function _initialiseProps() {
  var _this5 = this;

  this.setFieldsInitialValue = function (initialValues) {
    var flattenedInitialValues = _this5.flattenRegisteredFields(initialValues);
    var fieldsMeta = _this5.fieldsMeta;
    Object.keys(flattenedInitialValues).forEach(function (name) {
      if (fieldsMeta[name]) {
        _this5.setFieldMeta(name, _extends({}, _this5.getFieldMeta(name), {
          initialValue: flattenedInitialValues[name]
        }));
      }
    });
  };

  this.getAllValues = function () {
    var fieldsMeta = _this5.fieldsMeta,
        fields = _this5.fields;

    return Object.keys(fieldsMeta).reduce(function (acc, name) {
      return set(acc, name, _this5.getValueFromFields(name, fields));
    }, {});
  };

  this.getFieldsValue = function (names) {
    return _this5.getNestedFields(names, _this5.getFieldValue);
  };

  this.getFieldValue = function (name) {
    var fields = _this5.fields;

    return _this5.getNestedField(name, function (fullName) {
      return _this5.getValueFromFields(fullName, fields);
    });
  };

  this.getFieldsError = function (names) {
    return _this5.getNestedFields(names, _this5.getFieldError);
  };

  this.getFieldError = function (name) {
    return _this5.getNestedField(name, function (fullName) {
      return getErrorStrs(_this5.getFieldMember(fullName, 'errors'));
    });
  };

  this.isFieldValidating = function (name) {
    return _this5.getFieldMember(name, 'validating');
  };

  this.isFieldsValidating = function (ns) {
    var names = ns || _this5.getValidFieldsName();
    return names.some(function (n) {
      return _this5.isFieldValidating(n);
    });
  };

  this.isFieldTouched = function (name) {
    return _this5.getFieldMember(name, 'touched');
  };

  this.isFieldsTouched = function (ns) {
    var names = ns || _this5.getValidFieldsName();
    return names.some(function (n) {
      return _this5.isFieldTouched(n);
    });
  };
};

export default function createFieldsStore(fields) {
  return new FieldsStore(fields);
}